package com.zhangyu;

import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.eclipse.milo.opcua.sdk.client.api.config.OpcUaClientConfig;
import org.eclipse.milo.opcua.sdk.client.api.identity.AnonymousProvider;
import org.eclipse.milo.opcua.sdk.client.api.nodes.Node;
import org.eclipse.milo.opcua.stack.client.DiscoveryClient;
import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy;
import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText;
import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
import org.eclipse.milo.opcua.stack.core.types.structured.EndpointDescription;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.concurrent.ExecutionException;

import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint;

public class OpcUtil {

    public OpcUaClient createClient(String endpointUrl, SecurityPolicy securityPolicy) throws Exception {

        Path securityTempDir = Paths.get(System.getProperty("java.io.tmpdir"), "security");
        Files.createDirectories(securityTempDir);
        if (!Files.exists(securityTempDir)) {
            throw new Exception("unable to create security dir: " + securityTempDir);
        }
        KeyStoreLoader loader = new KeyStoreLoader().load(securityTempDir);

        List<EndpointDescription> endpoints;
        try {
            endpoints = DiscoveryClient.getEndpoints(endpointUrl).get();
        } catch (Throwable ex) {
            String discoveryUrl = endpointUrl;
            if (!discoveryUrl.endsWith("/")) {
                discoveryUrl += "/";
            }
            discoveryUrl += "discovery";
            endpoints = DiscoveryClient.getEndpoints(discoveryUrl).get();
        }

        EndpointDescription endpoint = endpoints.stream()
                .filter(e -> e.getSecurityPolicyUri().equals(securityPolicy.getUri())).findFirst()
                .orElseThrow(() -> new Exception("no desired endpoints returned"));

        OpcUaClientConfig config = OpcUaClientConfig.builder()
                .setApplicationName(LocalizedText.english("chanse opc-ua client"))
                .setApplicationUri("urn:chanse:opcua:client").setCertificate(loader.getClientCertificate())
                .setKeyPair(loader.getClientKeyPair()).setEndpoint(endpoint)
                .setIdentityProvider(new AnonymousProvider()).setRequestTimeout(uint(5000)).build();
        return OpcUaClient.create(config);
    }

    public void browseNode(String indent, OpcUaClient client, NodeId browseRoot, int level) {
        try {
            List<Node> nodes = client.getAddressSpace().browse(browseRoot).get();

            for (Node node : nodes) {
                System.out.println(level + ". " + indent + " Node=ns=" + node.getBrowseName().get().getNamespaceIndex()
                        + ";s=" + node.getBrowseName().get().getName());
                node.getNodeId().get().getType();
                // recursively browse to children
                browseNode(indent + "  ", client, node.getNodeId().get(), level + 1);
            }
        } catch (InterruptedException | ExecutionException e) {
            System.out.println("Browsing nodeId=" + browseRoot + " failed: " + e.getMessage());
        }
    }

}

